package com.acmen.easyapi.executor;

import com.acmen.easyapi.constant.AuthConstants;
import com.acmen.easyapi.dto.TestDataQry;
import com.acmen.easyapi.dto.UnitTestCmd;
import com.acmen.easyapi.model.*;
import com.acmen.easyapi.repository.ApiRepository;
import com.acmen.easyapi.repository.EnvironmentRepository;
import com.acmen.easyapi.util.MultiValueMapUtil;
import com.acmen.easyapi.util.RequestUtil;
import com.acmen.easyapi.util.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author acmen
 * 单元测试执行器
 */
@Component
public class UnitTestExe {
    @Autowired
    private EnvironmentRepository environmentRepository;
    private RestTemplate restTemplate = new RestTemplate();
    @Value("${acmen.easyapi.intranetIp:*}")
    private String intranetIp;
    @Autowired
    private ApiRepository apiRepository;
    /**
     * 获取接口调用结果
     *
     * @param unitTestCmd
     * @return
     */
    public Response test(UnitTestCmd unitTestCmd, HttpServletResponse response) {
        String ip = RequestUtil.getIpAddress();
        Cookie cookie = new Cookie("currentEnvId", unitTestCmd.getEnvId());
        cookie.setPath("/");
        cookie.setMaxAge(Integer.MAX_VALUE);//设置其生命周期
        response.addCookie(cookie);

        Environment environment = environmentRepository.getById(unitTestCmd.getEnvId());
        if(environment == null){
            List<Environment> environments = environmentRepository.getGatewayEnvironments(unitTestCmd.getAppName());
            environment = environments.stream().filter(x->x.getId().equals(unitTestCmd.getEnvId())).findFirst().get();
        }
        String url = environment.getBasePath()+unitTestCmd.getServletPath();

        if(!(ip.matches(intranetIp.replace("*",".*"))||ip.startsWith("127.0."))){
            List<Environment> gatewayEnvironments = environmentRepository.getGatewayEnvironments(unitTestCmd.getAppName());
            String finalUrl = url;
            boolean pass = gatewayEnvironments.stream().anyMatch(x -> finalUrl.startsWith(x.getBasePath()));
            //如果不是内网IP调用，只能调网关地址
            if(!pass){
                return Response.failure(110001,"你的网络不是内网IP，只能调网关地址！");
            }
        }
        Api api = apiRepository.selectById(unitTestCmd.getApiId());
        //--------------------------------------
        HttpMethod httpMethod = HttpMethod.valueOf(unitTestCmd.getRequestMethod());
        MultiValueMap<String, String> httpHeaders = getHeaders(unitTestCmd);
        MultiValueMap params = getParams(unitTestCmd);
        if(params!=null && !params.isEmpty()) {
            if(url.contains("?")){
                url += "&"+getQueryString(params);
            }else{
                url += "?"+getQueryString(params);
            }
        }
        ReqParamNode bodyNode = api.getReqParamNode().getSubNodes().stream().filter(e->e.getName().equalsIgnoreCase(ReqParamNode.NODE_BODY)).findFirst().orElse(null);
        if(bodyNode!=null){
            httpHeaders.set(HttpHeaders.CONTENT_TYPE , MediaType.APPLICATION_JSON.toString());
            Object body = getBody(unitTestCmd);
            if(body == null){
                body = new JSONObject();
            }
            HttpEntity requestEntity = new HttpEntity(body,httpHeaders);
            ResponseEntity<JSONObject> responseEntity = restTemplate.exchange(url, httpMethod, requestEntity, JSONObject.class);
            String data = StringUtil.formatJson(responseEntity.getBody().toJSONString());
            return Response.of(data);
        }else{
            HttpEntity requestEntity = new HttpEntity(httpHeaders);
            ResponseEntity<JSONObject> responseEntity = restTemplate.exchange(url, httpMethod, requestEntity, JSONObject.class);
            String data = StringUtil.formatJson(responseEntity.getBody().toJSONString());
            return Response.of(data);
        }
    }

    private Object getBody(UnitTestCmd unitTestCmd) {
        Object body = null;
        String requestBody = unitTestCmd.getRequestBody();
        if(StringUtil.isNotEmpty(requestBody)){
            body = JSON.parse(requestBody.trim());
        }
        return body;
    }

    private MultiValueMap getParams(UnitTestCmd unitTestCmd) {
        MultiValueMap params = new LinkedMultiValueMap<>();
        String requestParams = unitTestCmd.getRequestParams();
        if(StringUtil.isNotEmpty(requestParams)){
            JSONObject paramJson = JSON.parseObject(requestParams);
            params.addAll(MultiValueMapUtil.toMultiValueMap(paramJson));
        }
        return params;
    }

    private String getQueryString(MultiValueMap multiValueMap) {
        StringBuffer query = new StringBuffer();
        multiValueMap.forEach((key,value)->{
            if(value instanceof List){
                for(Object e : (List)value){
                    query.append(key +"="+e +"&");
                }
            }else if(value instanceof Map){
                ((Map)value).forEach((k,v)->{
                    query.append(key+"."+k +"="+v +"&");
                });
            }else{
                query.append(key +"="+value +"&");
            }
        });
        String queryString = query.toString();
        if(queryString.endsWith("&")){
            queryString = queryString.substring(0,queryString.length()-1);
        }
        return queryString;
    }


    private MultiValueMap getHeaders(UnitTestCmd unitTestCmd) {
        MultiValueMap headers = new HttpHeaders();
        String accessToken = unitTestCmd.getAccessToken();
        if(StringUtil.isNotEmpty(accessToken)){
            headers.set(AuthConstants.ACCESS_TOKEN,accessToken);
        }
        String jwt = unitTestCmd.getJwt();
        if(StringUtil.isNotEmpty(jwt)){
            headers.set(AuthConstants.JWT,jwt);
        }
        String requestHeaders = unitTestCmd.getRequestHeaders();
        if(StringUtil.isNotEmpty(requestHeaders)){
            JSONObject headerJson = JSON.parseObject(requestHeaders);
            headers.addAll(MultiValueMapUtil.toMultiValueMap(headerJson));
        }
        return headers;
    }

    /**
     * 获取测试数据
     * @param testDataQry
     * @return
     */
    public Response getTestData(TestDataQry testDataQry) {
        HttpHeaders httpHeaders = new HttpHeaders();
        if(StringUtil.isNotEmpty(testDataQry.getAccessToken())){
            httpHeaders.set(AuthConstants.ACCESS_TOKEN,testDataQry.getAccessToken());
        }
        if(StringUtil.isNotEmpty(testDataQry.getJwt())){
            httpHeaders.set(AuthConstants.JWT,testDataQry.getJwt());
        }

        Environment environment = environmentRepository.getById(testDataQry.getEnvId());
        if(environment == null){
            List<Environment> environments = environmentRepository.getGatewayEnvironments(testDataQry.getAppName());
            environment = environments.stream().filter(x->x.getId().equals(testDataQry.getEnvId())).findFirst().get();
        }
        String url = environment.getBasePath()+"/api/create-test-data/"+testDataQry.getApiId();
        HttpEntity requestEntity = new HttpEntity(httpHeaders);
        ResponseEntity<Response> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, Response.class);
        Response<Map> res = responseEntity.getBody();
        if(res.isSuccess()==false){
            return res;
        }
        Map result = new HashMap();
        RequestData requestData = ((JSONObject)JSON.toJSON(res.getData())).toJavaObject(RequestData.class);

        if(requestData.getHeaders()!=null){
            result.put("headers",StringUtil.formatJson(JSON.toJSONString(requestData.getHeaders())));
        }
        if(requestData.getBody()!=null){
            if(requestData.getBody() instanceof Map){
                if(!CollectionUtils.isEmpty(testDataQry.getBodyFields())){
                    JSONObject newJsonObject = new JSONObject();
                    for(String field : testDataQry.getBodyFields()){
                        newJsonObject.put(field,((JSONObject)requestData.getBody()).get(field));
                    }
                    result.put("body",StringUtil.formatJson(newJsonObject.toJSONString()));
                }else{
                    result.put("body",StringUtil.formatJson(JSON.toJSONString(requestData.getBody())));
                }
            }else{
                result.put("body",StringUtil.formatJson(JSON.toJSONString(requestData.getBody())));
            }
        }

        if(requestData.getParams()!=null){
            if(!CollectionUtils.isEmpty(testDataQry.getParameters())){
                JSONObject newJsonObject = new JSONObject();
                for(String param : testDataQry.getParameters()){
                    newJsonObject.put(param,requestData.getParams().get(param));
                }
                result.put("params",StringUtil.formatJson(newJsonObject.toJSONString()));
            }else{
                result.put("params",StringUtil.formatJson(JSON.toJSONString(requestData.getParams())));
            }
        }

        String servletPath = testDataQry.getPath();
        if(requestData.getPathVariables()!=null){
            for(Map.Entry<String,Object> entry : requestData.getPathVariables().entrySet()){
                servletPath =  servletPath.replace("{"+entry.getKey()+"}",entry.getValue()+"");
            }
        }
        result.put("servletPath",servletPath);
        return Response.of(result);
    }
}
